package com.hmdp.utils;

import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;

import java.time.LocalDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;

@Component
public class RedisIDWorker {
    /**
     * 初始时间戳秒数
     */
    private static final long BEGIN_TIMESTAMP = 1704067200L;
    /**
     * 序列号位数
     */
    private static final int SEQUENCE_BITS = 32;

    private StringRedisTemplate stringRedisTemplate;

    public RedisIDWorker(StringRedisTemplate stringRedisTemplate) {
        this.stringRedisTemplate = stringRedisTemplate;
    }

    /**
     * 获取自增后的全局唯一ID
     * 注意：单个key我们需要保证key不相同，即使是同一个业务内的key也不能相同。
     * Redis中对同一个key做自增，value最多到2^64，而我们的全局唯一ID（value）策略里，真正记录序列号的位数最多只有32bit
     * 因此我们在业务key前缀后面拼接一个当天日期，这样设计保证每天都自增的是一个新key的id，而一天的下单量不可能超过2^32个
     * @param keyPrefix 业务key前缀
     * @return 自增后的全局唯一ID
     */
    public long nextId(String keyPrefix) {
        // 生成时间戳部分
        LocalDateTime now = LocalDateTime.now();
        long nowSecond = now.toEpochSecond(ZoneOffset.UTC);
        long timestamp = nowSecond - BEGIN_TIMESTAMP;
        // 生成序列号部分
        // 获取当前日期，精确到天（好处1：避免自增相同key的value超过32位上限；好处2：根据key中的日期，方便进行统计）
        String date = now.format(DateTimeFormatter.ofPattern("yyyy:MM:dd"));
        String key;
        if (!keyPrefix.endsWith(":")) {
            key = "incr:" + keyPrefix + ":" + date;
        }else {
            key = "incr:" + keyPrefix + date;
        }
        // 根据key对value（分布式全局ID）做自增长，如果key不存在，则自动创建该key并返回自增后的结果
        long sequence = stringRedisTemplate.opsForValue().increment(key);
        // 返回自增后的全局唯一ID，将timestamp从最低位左移SEQUENCE_BITS位，低SEQUENCE_BITS位补0，再与sequence做或运算填充
        return timestamp << SEQUENCE_BITS | sequence;
    }
}
